package earlyDetection;

import com.google.common.collect.Sets;
import config.Config;
import earlyDetection.pg.PGEdge;
import network.Device;
import network.Network;
import network.Port;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultDirectedGraph;
import verifier.Ports.Ports;
import verifier.util.BDDEngine;
import verifier.util.PPM;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

public class PolicyCheckerNaiveSleep implements Runnable {
    private HashMap<Integer, Graph<Device, PGEdge>> ecToGraph = new HashMap<>();
    private Graph<Device, PGEdge> graph;
    private int hs;
    private Set<Device> closed = new HashSet<>();
    private BDDEngine bddEngine;
    private Network network;
    public boolean stop = false;
    private boolean foundValid = false;
    private Thread worker;
    private Automata automata;
    private long startedAt = 0;
    private Set<PGEdge> removed = new HashSet<>();
    private Set<PGEdge> history = new HashSet<>();
    private Set<PGEdge> needToRemove = new HashSet<>();
    private AtomicBoolean running = new AtomicBoolean(false);
    private CountDownLatch countDownLatch = new CountDownLatch(1);


    public PolicyCheckerNaiveSleep(Network network, Graph<Device, PGEdge> graph, BDDEngine bddEngine, int hs, Automata automata) {
        this.network = network;
        this.graph = graph;
        this.bddEngine = bddEngine;
        this.hs = hs;
        this.automata = automata;
    }

    public void interrupt(Device newClosed, HashMap<Integer, Ports> ecToPorts) {
        closed.add(newClosed);
        needToRemove = new HashSet<>();
        int ec = 0; // Warning: general case hase multiple overlapped ecs
        for (int e : ecToPorts.keySet()) {
            if (bddEngine.and(ec, this.hs) == 0) {
                ec = e;
                break;
            }
        }
        for (PGEdge edge : graph.outgoingEdgesOf(newClosed)) {
            if (!ecToPorts.get(ec).getAll().contains(edge.port)) {
                needToRemove.add(edge);
            }
        }
        removed.addAll(needToRemove);

        System.out.println("#closed: " + closed.size());

//        if (Sets.intersection(history, needToRemove).size() > 0) {
//            if (running.get()) {
//                System.exit(2);
//            }
//            worker.interrupt();
//            try {
//                countDownLatch.await();
                if (Sets.intersection(history, needToRemove).size() > 0) {
                    worker.interrupt();
                }
//            } catch (InterruptedException e) {

//            }
//        }
    }

    public void start() {
        worker = new Thread(this);
        worker.start();
    }

    public void run() {
        running.set(true);
        startedAt = System.nanoTime();
        traverse(network.getDevice("rsw-0-0"), graph, automata);
        stop = true;
        System.out.println("naive cannot reach!");
        System.out.println("naive #closed: " + closed.size());
        System.out.println("#processed updates: " + Config.processedUpdates);
        System.out.println("naive $time: " + (System.nanoTime() - Config.startAt));
    }

    private void traverse(Device current, Graph<Device, PGEdge> pg, Automata automata) {
        if (Sets.intersection(history, removed).size() > 0) return;
        int state = automata.getCurrentState();
        automata.take(current.getName());
        if (automata.isAccept()) {
            System.out.println("$foundValid: " + (System.nanoTime() - startedAt));
            foundValid = true;
            try {
                Thread.sleep(10000000);
            } catch (InterruptedException e) {
                startedAt = System.nanoTime();
                foundValid = false;
                automata.setCurrentState(state);
            }
            return;
        }
        if (automata.getCurrentState() == -1) {
            automata.setCurrentState(state);
            return;
        }
        for (PGEdge edge : pg.outgoingEdgesOf(current)) {
            if (removed.contains(edge)) {
                continue;
            }
            history.add(edge);
            traverse(edge.port.getPeerDevice(), pg, automata);
            history.remove(edge);
        }
        automata.setCurrentState(state);
    }
}